package com.linkhub.oms.admin.modules.system.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.excel.util.StringUtils;
import com.linkhub.oms.admin.common.enums.EmailSourceEnum;
import com.linkhub.oms.admin.common.util.I18nMessageUtil;
import com.linkhub.oms.admin.modules.cache.service.RedisService;
import com.linkhub.oms.admin.modules.system.domain.param.EmailCodeParam;
import com.linkhub.oms.admin.modules.system.domain.param.SystemSignInParam;
import com.linkhub.oms.admin.modules.system.domain.param.SystemSignUpParam;
import com.linkhub.oms.admin.modules.system.domain.vo.LhTokenInfoDTO;
import com.linkhub.oms.admin.modules.system.domain.vo.RegisterInfoVO;
import com.linkhub.oms.admin.modules.system.service.SystemService;
import com.linkhub.oms.admin.modules.user.account.service.IUserAccountService;
import com.linkhub.oms.admin.modules.user.domain.dto.UserDTO;
import com.linkhub.oms.admin.modules.user.service.UserBizService;
import com.linkhub.oms.admin.modules.whitelist.service.WhiteListBizService;
import com.linkhub.oms.common.enums.IEnumConvert;
import com.linkhub.oms.common.exception.BizException;
import com.linkhub.oms.common.util.PhoneNumberFormatUtil;
import com.linkhub.oms.common.util.TokenGenerator;
import com.linkhub.oms.component.email.service.LhMailService;
import com.linkhub.oms.dao.mpservice.ITemporaryCodeService;
import com.linkhub.oms.entity.TemporaryCodeEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Objects;

import static com.linkhub.oms.common.constant.ProjectConstant.LOGIN_PASSWORD_ERROR_TIMES;
import static com.linkhub.oms.common.constant.ProjectConstant.VERIFICATION_CODE_LENGTH;

/**
 * @Description: SystemService
 * @Author: echo
 * @Date: 2024/3/6 17:02
 * @Version: 1.0
 */
@Slf4j
@Service
public class SystemServiceImpl implements SystemService {

    @Resource
    private RedisService redisService;
    @Resource
    private UserBizService userBizService;
    @Resource
    private LhMailService lhMailService;
    @Resource
    private WhiteListBizService whiteListBizService;
    @Resource
    private ITemporaryCodeService temporaryCodeService;


    @Override
    @SuppressWarnings("all")
    public void sendEmailCode(EmailCodeParam emailCodeParam) {
        // 获取验证码
        String code = RandomUtil.randomNumbers(VERIFICATION_CODE_LENGTH);
        log.info("sendEmailCode: [{}]", code);
        // 设置到redis
        redisService.bindEmailCode(emailCodeParam.getEmail(), code);

        // 发送验证码
        EmailSourceEnum emailSource = IEnumConvert.valueOf(emailCodeParam.getSource(), EmailSourceEnum.class);
        lhMailService.sendGmailCode(emailSource.getEmailSubject(), emailCodeParam.getEmail(), code);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public RegisterInfoVO signUp( SystemSignUpParam signUpParam) {
        RegisterInfoVO registerInfoVO = new RegisterInfoVO();
        checkSignUpParam(signUpParam, true);
        String email = signUpParam.getEmail();
        UserDTO user = userBizService.getUserByEmail(email);
        if (Objects.nonNull(user)) {
            throw new BizException(I18nMessageUtil.getMessage("signUp.user.present"));
        }

        redisService.unBundleEmailCode(email);

        String userId = userBizService.addSignUpCustomer(signUpParam);

        boolean isVip = whiteListBizService.exists(signUpParam.getEmail());
        if (isVip) {
            String temporaryUniqueToken = TokenGenerator.generateUniqueToken();
            TemporaryCodeEntity temporaryCodeEntity = new TemporaryCodeEntity();
            temporaryCodeEntity.setSourceCode(userId);
            temporaryCodeEntity.setTemporaryCode(temporaryUniqueToken);
            temporaryCodeService.save(temporaryCodeEntity);

            registerInfoVO.setTemporaryCode(temporaryUniqueToken);
        }
        registerInfoVO.setVip(isVip);
        return registerInfoVO;
    }


    @Override
    public LhTokenInfoDTO login(SystemSignInParam signInParam) {
        String email = signInParam.getEmail();
        // 根据邮箱查用户，判断用户是否存在
        UserDTO user = userBizService.getUserByEmail(email);
        if (Objects.isNull(user)) {
            throw new BizException(I18nMessageUtil.getMessage("login.user.absent"));
        }

        // 用户密码错误次数是否被锁定，>= 5
        int times = redisService.getPassWordErrorTimes(email);
        if (times >= LOGIN_PASSWORD_ERROR_TIMES) {
            throw new BizException(I18nMessageUtil.getMessage("login.account.lock"));
        }

        // 判断密码是否正确
        if (!Objects.equals(user.getPassword(), signInParam.getPassword())) {
            int errorTimes = redisService.incrPassWordErrorTimes(email);
            String errorMsg = errorTimes >= LOGIN_PASSWORD_ERROR_TIMES
                    ? I18nMessageUtil.getMessage("login.account.lock")
                    : I18nMessageUtil.getMessage("login.password.error", LOGIN_PASSWORD_ERROR_TIMES - errorTimes);
            throw new BizException(errorMsg);
        }

        // 登录
        StpUtil.login(user.getUserId());
        redisService.delPasswordErrorKey(email);

        return LhTokenInfoDTO.create(StpUtil.getTokenInfo());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resetCipher(SystemSignUpParam signUpParam) {

        checkSignUpParam(signUpParam, false);

        UserDTO user = userBizService.getUserByEmail(signUpParam.getEmail());
        if (Objects.isNull(user)) {
            throw new BizException(I18nMessageUtil.getMessage("reset.user.absent"));
        }

        userBizService.resetCipher(signUpParam.getEmail(), signUpParam.getPassword());

    }

    private void checkSignUpParam(SystemSignUpParam signUpParam, boolean isSignUp) {
        // 检查注册必填参数
        if (isSignUp) {
            String userName = signUpParam.getUserName();
            if (StringUtils.isBlank(userName)) {
                throw new BizException(I18nMessageUtil.getMessage("validation.userName.notBlank"));
            }
            String countryDigitalCode = signUpParam.getCountryDigitalCode();
            if (StringUtils.isBlank(countryDigitalCode)) {
                throw new BizException(I18nMessageUtil.getMessage("validation.countryDigitalCode.notBlank"));
            }
            String phone = signUpParam.getPhone();
            if (StringUtils.isBlank(phone)) {
                throw new BizException(I18nMessageUtil.getMessage("validation.phone.notBlank"));
            }

            // 手机号码格式验证
            boolean validPhone = PhoneNumberFormatUtil.isValidPhone(countryDigitalCode, phone);
            if (!validPhone) {
                throw new BizException(I18nMessageUtil.getMessage("signUp.phone.invalid"));
            }
        }
        // 密码是否一致
        if (!Objects.equals(signUpParam.getPassword(), signUpParam.getConfirmPassword())) {
            throw new BizException(I18nMessageUtil.getMessage("signUp.password.error"));
        }

        // 验证码超时、邮箱错误、验证码不正确
        String email = signUpParam.getEmail();
        String code = redisService.getCodeByEmail(email);
        if (!Objects.equals(code, signUpParam.getCode())) {
            throw new BizException(I18nMessageUtil.getMessage("signUp.email.code.notMatch"));
        }
    }
}
